#pragma once
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Util.hpp"
#include <string>
#include <unordered_map>
#include <memory>
#include <functional>

class tcp_server
{
protected:
using func_t = std::function<void()>;
struct connection;
using conn_ptr = std::shared_ptr<connection>;

    struct connection
    {
        int _sockfd;
        std::string _read_buffer;
        std::string _write_buffer;

        func_t _recver;// 读回调函数
        func_t _writer;// 写回调函数
        func_t _excepter;// 异常回调函数

        void regiset_callback(func_t r,func_t w,func_t e)
        {
            _recver = r;
            _writer = w;
            _excepter = e;
        }

        connection(int sockfd = -1)
            :_sockfd(sockfd)
        {}
    };

    void add_connection(int sockfd,uint32_t events,func_t r,func_t w,func_t e)
    {
        // 如果是ET模式，就要设置成非阻塞状态
        if(events & EPOLLET) util::set_noblocking(sockfd);

        _ep.add_event(_sock.get_fd(),events);// 注册事件

        conn_ptr cp(new connection(sockfd));
        cp->regiset_callback(r,w,e);// 注册回调函数
        _conns.insert(std::make_pair(sockfd,cp));
    }

public:
    tcp_server(short port)
        :_port(port)
    {
        _sock.Socket();
        _sock.Bind(_port);
        _sock.Listen();
        _ep.create();
        add_connection(_sock.get_fd(),EPOLLIN | EPOLLET,std::bind(&sock::Accept,&_sock),nullptr,nullptr);
    }

    void start()
    {
        while(true)
        {
            int ret = _ep.wait(-1);// 阻塞式
            if(ret == 0) continue;
            else if(ret == -1)
            {
                LOG(FATAL,"epoll_wait error!");
                exit(EPOLL_WAIT_ERR);
            }

            for(int i=0;i<ret;i++)
            {
                if(_ep.get_events_fd(i) == _sock.get_fd() && _ep.get_events(i) & EPOLLIN)
                {   
                    _conns[_ep.get_events_fd(i)]->_recver();
                }
            }
        }
    }
protected:
    short _port;
    sock _sock;
    epoll _ep;
    std::unordered_map<int,conn_ptr> _conns;
};